package main

import (
	"fmt"
	"math"
)

// 定义结构体
type Point struct {
	X, Y float64
}

//type Rectangle struct {
//	Width, Height float64
//}
//
//type Circle struct {
//	Radius float64
//}

// 为Point定义方法 - 值接收者
func (p Point) Distance(other Point) float64 {
	dx := p.X - other.X
	dy := p.Y - other.Y
	return math.Sqrt(dx*dx + dy*dy)
}

// 为Point定义方法 - 指针接收者
func (p *Point) Scale(factor float64) {
	p.X *= factor
	p.Y *= factor
}

// 为Point定义方法 - 修改值
func (p *Point) Move(dx, dy float64) {
	p.X += dx
	p.Y += dy
}

// 为Rectangle定义方法 - 值接收者
func (r Rectangle) Area() float64 {
	return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
	return 2 * (r.Width + r.Height)
}

// 为Rectangle定义方法 - 指针接收者
func (r *Rectangle) Scale(factor float64) {
	r.Width *= factor
	r.Height *= factor
}

// 为Circle定义方法
func (c Circle) Area() float64 {
	return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
	return 2 * math.Pi * c.Radius
}

func (c *Circle) Scale(factor float64) {
	c.Radius *= factor
}

func main() {
	fmt.Println("12. 方法")

	// 方法的基本使用
	fmt.Println("\n--- 方法的基本使用 ---")

	// 创建点实例
	p1 := Point{X: 0, Y: 0}
	p2 := Point{X: 3, Y: 4}

	// 调用值接收者方法
	distance := p1.Distance(p2)
	fmt.Printf("点 p1%+v 到 p2%+v 的距离: %.2f\n", p1, p2, distance)

	// 调用指针接收者方法
	fmt.Printf("缩放前 p1: %+v\n", p1)
	p1.Scale(2)
	fmt.Printf("缩放后 p1: %+v\n", p1)

	// 移动点
	fmt.Printf("移动前 p2: %+v\n", p2)
	p2.Move(1, 1)
	fmt.Printf("移动后 p2: %+v\n", p2)

	// 值接收者 vs 指针接收者
	fmt.Println("\n--- 值接收者 vs 指针接收者 ---")

	// 值接收者不会修改原始值
	rect1 := Rectangle{Width: 10, Height: 5}
	fmt.Printf("原始矩形 rect1: %+v, 面积: %.2f\n", rect1, rect1.Area())

	// 通过值调用Scale方法
	scaledRect := rect1
	scaledRect.Scale(2)
	fmt.Printf("scaledRect: %+v, 面积: %.2f\n", scaledRect, scaledRect.Area())
	fmt.Printf("rect1 未改变: %+v, 面积: %.2f\n", rect1, rect1.Area())

	// 指针接收者会修改原始值
	rect2 := Rectangle{Width: 10, Height: 5}
	fmt.Printf("原始矩形 rect2: %+v, 面积: %.2f\n", rect2, rect2.Area())

	// 通过指针调用Scale方法
	rect2.Scale(2)
	fmt.Printf("rect2 被修改: %+v, 面积: %.2f\n", rect2, rect2.Area())

	// 使用指针调用方法
	rect3 := &Rectangle{Width: 3, Height: 4}
	fmt.Printf("矩形 rect3: %+v, 面积: %.2f\n", *rect3, rect3.Area())
	rect3.Scale(3)
	fmt.Printf("缩放后 rect3: %+v, 面积: %.2f\n", *rect3, rect3.Area())

	// 方法表达式和方法值
	fmt.Println("\n--- 方法表达式和方法值 ---")

	// 方法值 - 将方法绑定到特定的接收者
	distanceFromOrigin := p1.Distance
	fmt.Printf("从原点到 p1 的距离: %.2f\n", distanceFromOrigin(Point{0, 0}))

	// 方法表达式 - 未绑定到特定接收者
	distanceFunc := Point.Distance
	fmt.Printf("p1 到 p2 的距离: %.2f\n", distanceFunc(p1, p2))

	// 指针接收者的方法表达式
	scaleFunc := (*Point).Scale
	scaleFunc(&p1, 0.5)
	fmt.Printf("通过方法表达式缩放后 p1: %+v\n", p1)

	// 不同类型的方法
	fmt.Println("\n--- 不同类型的方法 ---")

	circle := Circle{Radius: 5}
	fmt.Printf("圆形: %+v, 面积: %.2f, 周长: %.2f\n", circle, circle.Area(), circle.Perimeter())

	circle.Scale(2)
	fmt.Printf("缩放后圆形: %+v, 面积: %.2f, 周长: %.2f\n", circle, circle.Area(), circle.Perimeter())

	// 嵌套结构体的方法
	fmt.Println("\n--- 嵌套结构体的方法 ---")

	type Address struct {
		Street string
		City   string
	}

	type Person struct {
		Name    string
		Age     int
		Address // 嵌入结构体
	}

	// 为嵌入结构体定义方法
	func (a Address) FullAddress() string {
		return a.Street + ", " + a.City
	}

	person := Person{
		Name: "张三",
		Age:  30,
		Address: Address{
			Street: "中山路123号",
			City:   "北京",
		},
	}

	fmt.Printf("姓名: %s\n", person.Name)
	fmt.Printf("年龄: %d\n", person.Age)
	fmt.Printf("完整地址: %s\n", person.FullAddress()) // 可以直接调用嵌入结构体的方法

	// 方法与接口
	fmt.Println("\n--- 方法与接口 ---")

	// 定义接口
	type Geometry interface {
		Area() float64
		Perimeter() float64
	}

	// 不同类型实现相同接口
	shapes := []Geometry{
		Rectangle{Width: 4, Height: 3},
		Circle{Radius: 2},
	}

	for _, shape := range shapes {
		fmt.Printf("面积: %.2f, 周长: %.2f\n", shape.Area(), shape.Perimeter())
	}

	// 方法链
	fmt.Println("\n--- 方法链 ---")

	type Counter struct {
		Value int
	}

	func (c *Counter) Increment() *Counter {
		c.Value++
		return c
	}

	func (c *Counter) Add(x int) *Counter {
		c.Value += x
		return c
	}

	func (c *Counter) Reset() *Counter {
		c.Value = 0
		return c
	}

	counter := &Counter{}
	result := counter.Increment().Add(5).Increment()
	fmt.Printf("计数器值: %d\n", result.Value)

	// 重置计数器
	counter.Reset()
	fmt.Printf("重置后计数器值: %d\n", counter.Value)
}
